package com.leyou.order.service;

import com.leyou.common.auth.entity.UserInfo;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exception.LyException;
import com.leyou.common.utils.BeanHelper;
import com.leyou.common.utils.IdWorker;
import com.leyou.common.utils.UserHolder;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.SkuDTO;
import com.leyou.order.dto.CartDTO;
import com.leyou.order.dto.OrderDTO;
import com.leyou.order.entity.Order;
import com.leyou.order.entity.OrderDetail;
import com.leyou.order.entity.OrderLogistics;
import com.leyou.order.enums.OrderStatusEnum;
import com.leyou.order.mapper.OrderDetailMapper;
import com.leyou.order.mapper.OrderLogisticsMapper;
import com.leyou.order.mapper.OrderMapper;
import com.leyou.order.utils.PayHelper;
import com.leyou.order.vo.OrderDetailVO;
import com.leyou.order.vo.OrderLogisticsVO;
import com.leyou.order.vo.OrderVO;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.AddressDTO;
//import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private OrderLogisticsMapper logisticsMapper;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private ItemClient itemClient;

    @Autowired
    private UserClient userClient;


    /**
     * 创建订单
     * @param orderDTO
     * @return
     */
    //@GlobalTransactional // seata的分布式事务实现
    @Transactional(rollbackFor = RuntimeException.class) // 注意要加事务: 只能控制本地：当前线程下事务，控制不了分布式事务
    public Long createOrder(OrderDTO orderDTO) {
        // 订单ID
        long orderId = idWorker.nextId();
        // 当前用户
        UserInfo user = UserHolder.getUser();
        // 实例化一个订单对象
        Order order = new Order();
        order.setOrderId(orderId); // 订单编号
        order.setPostFee(0L); // 我们全场包邮
        order.setPaymentType(1); // 支付类型
        order.setUserId(user.getId()); // 当前用户
        order.setStatus(OrderStatusEnum.INIT.value()); // 订单状态
        order.setInvoiceType(0); // 无发票
        order.setSourceType(2); // pc端

        order.setActualFee(1L/*实付金额 = 总金额 - 活动的金额*/); // 实付金额： 为了测试，我们写一分钱，便于测试

        // 先获取购物车的数据
        List<CartDTO> carts = orderDTO.getCarts();
        // 把这个list集合转成map，便于通过skuid查询数量
        Map<Long, Integer> cartMap = carts.stream().collect(Collectors.toMap(CartDTO::getSkuId, CartDTO::getNum));
        // 收集所有的sku的id
        List<Long> skuIds = carts.stream().map(CartDTO::getSkuId).collect(Collectors.toList());
        // 批量查询sku集合
        List<SkuDTO> skuList = itemClient.querySkuListByIds(skuIds);
        // 计算所有sku的价格
        long total = skuList.stream().mapToLong(sku -> sku.getPrice() * cartMap.get(sku.getId())).sum();
        //总金额 = 购物车中所有的sku的价格之和；   sku的价格 = 单个sku价格 * num
        order.setTotalFee(total);
        // 保存订单表
        int count = orderMapper.insertSelective(order);
        if(count != 1){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }

        //================================================================================
        // 准备一个订单条目的集合
        List<OrderDetail> orderDetailList = new ArrayList<>();
        // 遍历sku集合，创建订单条目对象
        for (SkuDTO skuDTO : skuList) {
            OrderDetail od = new OrderDetail();
            od.setId(idWorker.nextId()); // 订单详情自己的id
            od.setOrderId(orderId);
            od.setSkuId(skuDTO.getId());
            od.setNum(cartMap.get(skuDTO.getId()));// 数量
            od.setTitle(skuDTO.getTitle());
            od.setPrice(skuDTO.getPrice());
            od.setOwnSpec(skuDTO.getOwnSpec());
            od.setImage(StringUtils.substringBefore(skuDTO.getImages(), ","));
            // 赋值
            orderDetailList.add(od);
        }
        // 保存订单详情
        count = orderDetailMapper.insertList(orderDetailList);
        if(count != orderDetailList.size()){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }
        // ===============================================================================

        // 查询地址信息
        AddressDTO addressDTO = userClient.queryAddressById(user.getId(), orderDTO.getAddressId());
        OrderLogistics orderLogistics = BeanHelper.copyProperties(addressDTO, OrderLogistics.class);
        orderLogistics.setOrderId(orderId); // 设置订单编号
        // 保存物流表信息
        count = logisticsMapper.insertSelective(orderLogistics);
        if(count != 1){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }

        //==================================================================================
        // 减库存： feign会把异常传递到当前服务中
        itemClient.minusStock(cartMap);
        //int i = 1/0; // 模拟异常
        //=================================================================================
        // 返回订单ID
        return orderId;
    }

    /**
     * 根据订单Id查询订单信息
     * @param id
     * @return
     */
    public OrderVO queryOrderById(Long id) {
        // 1、查询订单
        Order order = orderMapper.selectByPrimaryKey(id);
        if(order == null){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        // 2、判断订单归属
        UserInfo user = UserHolder.getUser();
        if(!order.getUserId().equals(user.getId())){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        // 3、查询订单条目
        OrderDetail od = new OrderDetail();
        od.setOrderId(id);
        List<OrderDetail> orderDetailList = orderDetailMapper.select(od);
        if(CollectionUtils.isEmpty(orderDetailList)){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        // 4、查询物流信息
        OrderLogistics orderLogistics = logisticsMapper.selectByPrimaryKey(id);
        if(orderLogistics == null){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        // 5、把数据封装到VO中
        OrderVO orderVO = BeanHelper.copyProperties(order, OrderVO.class);
        orderVO.setDetailList(BeanHelper.copyWithCollection(orderDetailList, OrderDetailVO.class));
        orderVO.setLogistics(BeanHelper.copyProperties(orderLogistics, OrderLogisticsVO.class));
        // 6、返回
        return orderVO;
    }

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private PayHelper payHelper;

    private static final String KEY_PREFIX = "ly:order:url:";

    /**
     * 获取支付链接：
     * @param id    订单ID  ，订单状态一定是未付款的
     * @return      支付链接
     */
    public String getPayUrl(Long id) {
        String key = KEY_PREFIX + id;
        // 1、根据id查询订单：去redis中去查
        try {
            String cacheUrl = redisTemplate.opsForValue().get(key);
            if(StringUtils.isNotBlank(cacheUrl)){
                return cacheUrl;
            }
        } catch (Exception e) {
            log.error("【订单微服务】- 生成二维码链接 - redis挂了【管理员赶紧看看】");
        }

        // 2、判断订单的状态：订单的状态，1、未付款 2、已付款,未发货 3、已发货,未确认 4、确认收货，交易成功 5、交易取消，订单关闭 6、交易结束，已评价
        Order order = orderMapper.selectByPrimaryKey(id);
        if(!OrderStatusEnum.INIT.value().equals(order.getStatus())){
            throw new LyException(ExceptionEnum.INVALID_ORDER_STATUS);
        }

        // 3、调用微信支付接口生成支付链接
        String payUrl = payHelper.getPayUrl(id, order.getActualFee(), "【乐优商城】商品购买信息");// 测试的金额我们在生成订单的时候写死一分钱

        // 4、返回支付链接前缓存url： 支付链接的有效期  2小时：  存入redis，key：订单的编号
        try {
            redisTemplate.opsForValue().set(key, payUrl, 2, TimeUnit.HOURS);
        } catch (Exception e) {
            log.error("【订单微服务】- 生成二维码链接 - redis挂了【管理员赶紧看看】");
        }

        // 5、返回
        return payUrl;
    }

    /**
     * 接收微信的支付结果，并且修改订单状态
     * @param wxmsg     微信的返回值
     * @return
     */
    @Transactional // 记得加入事务
    public Map<String, String> wxnotify(Map<String, String> wxmsg) {
        System.out.println("s 微信通知我们了。。。。。。");
        // 返回给微信的通知
        Map<String, String> msg = new HashMap<>();
        msg.put("return_code", "SUCCESS");
        msg.put("return_msg", "OK");
        try{
            // 校验标识
            payHelper.checkResultCode(wxmsg);
            // 校验签名
            payHelper.checkSignature(wxmsg);
            // 取出结果： 取出订单的id  和 支付金额
            String out_trade_no = wxmsg.get("out_trade_no");
            String total_fee = wxmsg.get("total_fee");
            if(StringUtils.isBlank(out_trade_no) || StringUtils.isBlank(total_fee)){
                throw new LyException(ExceptionEnum.INVALID_PARAM_ERROR);
            }
            // 转成数字
            Long orderId = Long.valueOf(out_trade_no); // 订单id
            Long totalFee = Long.valueOf(total_fee); // 支付金额
            // 查询订单
            Order order = orderMapper.selectByPrimaryKey(orderId);
            // 校验金额是否一致： 微信返回的支付金额和我们订单表中实际金额比较
            if(!totalFee.equals(order.getActualFee())){
                throw new LyException(ExceptionEnum.INVALID_PARAM_ERROR);
            }
            // 我们做接口幂等性校验：如果已经支付，不需要更新数据库
            if(!OrderStatusEnum.INIT.value().equals(order.getStatus())){
                return msg;
            }
            //=====================================================================
            // 修改状态
            Order record = new Order();
            record.setOrderId(orderId);
            record.setStatus(OrderStatusEnum.PAY_UP.value());
            record.setPayTime(new Date());
            int count = orderMapper.updateByPrimaryKeySelective(record);
            if(count != 1){
                throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
            }
            //=====================================================================
            log.info("【乐优支付】处理微信支付结果成功");
        }catch (Exception e){
            log.error("【乐优支付】处理微信支付结果的时候出现异常： {}", e.getMessage());
        }

        return msg;
    }

    /**
     * 查询订单状态
     * @param id
     * @return
     */
    public Integer queryOrderState(Long id) {
        Order order = orderMapper.selectByPrimaryKey(id);
        if(order == null){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        return order.getStatus();
    }
}
